package com.getpebble.android.framework.pebblekit;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Bundle;
import com.getpebble.android.PebbleApplication;
import com.getpebble.android.common.core.trace.Trace;
import com.getpebble.android.common.core.util.PblPreferences;
import com.getpebble.android.common.core.util.PblPreferences.PrefKey;
import com.getpebble.android.common.model.PblDevice;
import com.getpebble.android.framework.appmessage.AppMessage;
import com.getpebble.android.framework.appmessage.AppMessage.AppMessageCommand;
import com.getpebble.android.framework.appmessage.JsonPebbleDictionary;
import com.getpebble.android.framework.comm.MessageRouter;
import com.getpebble.android.framework.datalogging.Datalogging;
import com.getpebble.android.framework.endpoint.EndpointRequest;
import com.getpebble.android.framework.endpoint.EndpointRequest.EndpointAction;
import com.getpebble.android.framework.endpoint.EndpointRequest.EndpointArgumentKeys;
import com.getpebble.android.framework.notification.PblNotificationProcessor;
import com.getpebble.android.framework.protocol.EndpointId;
import com.getpebble.android.notifications.model.PblNotification;
import com.getpebble.android.notifications.model.PblNotification.LegacyNotification;
import com.getpebble.android.notifications.model.PblNotification.Source;
import com.google.common.base.Strings;
import com.google.gson.Gson;
import com.google.gson.JsonParseException;
import com.google.gson.reflect.TypeToken;
import java.io.Serializable;
import java.util.List;
import java.util.UUID;
import org.json.JSONException;

public class PebbleKitReceiver extends BroadcastReceiver {
    private static final String TAG = PebbleKitReceiver.class.getSimpleName();
    private static final Gson sGson = new Gson();

    private static class PebbleAlertNotificationData {
        public final String body;
        public final String title;
    }

    public void onReceive(Context context, Intent intent) {
        if (intent == null) {
            Trace.warning(TAG, "intent is null");
        } else if (intent.getAction() == null) {
            Trace.warning(TAG, "intent.getAction() is null");
        } else {
            String action = intent.getAction();
            Trace.verbose(TAG, "action = " + action);
            if ("com.getpebble.action.dl.ACK_DATA".equals(action)) {
                handleDataloggingAck(intent, context);
            } else if ("com.getpebble.action.dl.REQUEST_DATA".equals(action)) {
                handleDataloggingRequestData(intent, context);
            } else if ("com.getpebble.action.app.ACK".equals(action)) {
                handleAppMessageAck(intent, context);
            } else if ("com.getpebble.action.app.NACK".equals(action)) {
                handleAppMessageNack(intent, context);
            } else if ("com.getpebble.action.app.SEND".equals(action)) {
                handleAppMessageClientSend(intent, context);
            } else if ("com.getpebble.action.app.START".equals(action)) {
                handleAppStart(intent, context);
            } else if ("com.getpebble.action.app.STOP".equals(action)) {
                handleAppStop(intent, context);
            } else if ("com.getpebble.action.app.CONFIGURE".equals(action)) {
                handleConfigureApp(intent, context);
            } else if ("com.getpebble.action.SEND_NOTIFICATION".equals(action)) {
                handleNotification(intent, context);
            }
        }
    }

    private void handleAppStart(Intent intent, Context context) {
        UUID appUuid = getUuid(intent, "uuid");
        if (appUuid == null) {
            Trace.warning(TAG, "No UUID in Start message");
            return;
        }
        Bundle args = new Bundle();
        args.putString(EndpointArgumentKeys.UUID.toString(), appUuid.toString());
        sendRequestToConnectedWatch(context, new EndpointRequest(EndpointId.APP_LIFECYCLE, EndpointAction.START_APP, args));
    }

    private void handleAppStop(Intent intent, Context context) {
        UUID appUuid = getUuid(intent, "uuid");
        if (appUuid == null) {
            Trace.warning(TAG, "No UUID in Stop message");
            return;
        }
        Bundle args = new Bundle();
        args.putString(EndpointArgumentKeys.UUID.toString(), appUuid.toString());
        sendRequestToConnectedWatch(context, new EndpointRequest(EndpointId.APP_LIFECYCLE, EndpointAction.STOP_APP, args));
    }

    private void handleConfigureApp(Intent intent, Context context) {
        int targetApp = intent.getIntExtra("app_type", -1);
        if (targetApp == 1 || targetApp == 0) {
            String customName = intent.getStringExtra("name");
            Bitmap customIcon = (Bitmap) intent.getParcelableExtra("icon");
            Bundle args = new Bundle();
            args.putString(EndpointArgumentKeys.APP_TITLE.toString(), customName);
            args.putParcelable(EndpointArgumentKeys.BITMAP.toString(), customIcon);
            args.putInt(EndpointArgumentKeys.APP_TYPE.toString(), targetApp);
            sendRequestToConnectedWatch(context, new EndpointRequest(EndpointId.APP_CUSTOMIZE, EndpointAction.CUSTOMIZE_APP, args));
            return;
        }
        Trace.info(TAG, "Received customization message for unknow app type: " + targetApp);
    }

    private void handleAppMessageAck(Intent intent, Context context) {
        int transactionId = intent.getIntExtra("transaction_id", -1);
        if (transactionId == -1) {
            Trace.info(TAG, "handleAppMessageAck: transactionId not set");
            return;
        }
        AppMessage appMessage = new AppMessage((byte) (transactionId & 255), null, AppMessageCommand.ACK, null);
        Bundle args = new Bundle();
        args.putParcelable(EndpointArgumentKeys.APP_MESSAGE.toString(), appMessage);
        sendRequestToConnectedWatch(context, new EndpointRequest(EndpointId.APP_MESSAGE, EndpointAction.PUSH_APP_MESSAGE, args));
    }

    private void handleAppMessageNack(Intent intent, Context context) {
        int transactionId = intent.getIntExtra("transaction_id", -1);
        if (transactionId == -1) {
            Trace.info(TAG, "handleAppMessageNack: transactionId not set");
            return;
        }
        AppMessage appMessage = new AppMessage((byte) (transactionId & 255), null, AppMessageCommand.NACK, null);
        Bundle args = new Bundle();
        args.putParcelable(EndpointArgumentKeys.APP_MESSAGE.toString(), appMessage);
        sendRequestToConnectedWatch(context, new EndpointRequest(EndpointId.APP_MESSAGE, EndpointAction.PUSH_APP_MESSAGE, args));
    }

    private void handleAppMessageClientSend(Intent intent, Context context) {
        try {
            int transactionId = intent.getIntExtra("transaction_id", -1);
            UUID appUuid = getUuid(intent, "uuid");
            if (appUuid == null) {
                Trace.warning(TAG, "No UUID in AppMessage");
                return;
            }
            AppMessage appMessage = new AppMessage((byte) (transactionId & 255), appUuid, AppMessageCommand.PUSH, JsonPebbleDictionary.fromJson(intent.getStringExtra("msg_data")));
            Bundle args = new Bundle();
            args.putParcelable(EndpointArgumentKeys.APP_MESSAGE.toString(), appMessage);
            sendRequestToConnectedWatch(context, new EndpointRequest(EndpointId.APP_MESSAGE, EndpointAction.PUSH_APP_MESSAGE, args));
        } catch (JSONException je) {
            Trace.warning(TAG, "handleAppMessageClientSend: Could not parse json body: " + null, je);
        }
    }

    private void handleDataloggingAck(Intent intent, Context context) {
        try {
            UUID logUuid = getUuid(intent, "data_log_uuid");
            if (logUuid == null) {
                Trace.warning(TAG, "logUuid is null");
                return;
            }
            int dataId = intent.getIntExtra("pbl_data_id", -1);
            if (dataId == -1) {
                Trace.warning(TAG, "dataId is invalid");
            } else {
                getDatalogging(context).handleClientAck(logUuid, dataId);
            }
        } catch (Exception e) {
            Trace.warning(TAG, "Error handling datalogging ack", e);
        }
    }

    private void handleDataloggingRequestData(Intent intent, Context context) {
        try {
            UUID appUuid = (UUID) intent.getSerializableExtra("uuid");
            if (appUuid == null) {
                Trace.warning(TAG, "appUuid is null");
            } else {
                getDatalogging(context).handleClientRequestData(appUuid);
            }
        } catch (IllegalArgumentException e) {
            Trace.warning(TAG, "Error handling datalogging ack", e);
        }
    }

    private UUID getUuid(Intent intent, String key) {
        UUID uuid = null;
        Serializable serializableExtraUuid = intent.getSerializableExtra(key);
        if (serializableExtraUuid == null) {
            return uuid;
        }
        if (serializableExtraUuid instanceof UUID) {
            return (UUID) serializableExtraUuid;
        }
        if (!(serializableExtraUuid instanceof String)) {
            return uuid;
        }
        try {
            return UUID.fromString((String) serializableExtraUuid);
        } catch (Exception e) {
            Trace.warning(TAG, "Error getting UUID from String", e);
            return uuid;
        }
    }

    private void handleNotification(Intent intent, Context context) {
        String messageType = intent.getStringExtra("messageType");
        String intentSender = intent.getStringExtra("sender");
        String notificationData = intent.getStringExtra("notificationData");
        if (context == null) {
            Trace.error(TAG, "handleNotification: context was null");
            return;
        }
        PblPreferences prefs = new PblPreferences(context);
        if (Strings.isNullOrEmpty(messageType) || Strings.isNullOrEmpty(intentSender) || Strings.isNullOrEmpty(notificationData)) {
            Trace.warning(TAG, "Received a malformed PebbleNotification intent");
        } else if (!prefs.getBooleanData(PrefKey.ALLOW_THIRD_PARTY_NOTIFICATIONS, true)) {
            Trace.info(TAG, "Ignoring 3rd-party notification from: " + String.valueOf(intentSender));
        } else if (messageType.equals("PEBBLE_ALERT")) {
            try {
                List<PebbleAlertNotificationData> l = (List) sGson.fromJson(notificationData, new TypeToken<List<PebbleAlertNotificationData>>() {
                }.getType());
                if (l.size() != 1) {
                    Trace.warning(TAG, "Received malformed PebbleAlertNotificationData");
                    return;
                }
                PebbleAlertNotificationData data = (PebbleAlertNotificationData) l.get(0);
                LegacyNotification content = new LegacyNotification();
                content.title = data.title;
                content.body = data.body;
                PblNotificationProcessor.processNotification(PblNotification.from(content, Source.PEBBLEKIT));
            } catch (JsonParseException jpe) {
                Trace.warning(TAG, "Received malformed PebbleAlertNotificationData", jpe);
            } catch (Exception e) {
                Trace.error(TAG, "Caught an unexpected exception while parsing PebbleAlertNotificationData", e);
            }
        } else {
            Trace.warning(TAG, "Don't know how to handle messageType = " + messageType);
        }
    }

    protected Datalogging getDatalogging(Context context) {
        return Datalogging.getInstance(context);
    }

    protected void sendRequestToConnectedWatch(Context context, EndpointRequest request) {
        PblDevice connectedDevice = PebbleApplication.getConnectedDevice();
        if (connectedDevice == null) {
            Trace.info(TAG, "Can't send message to watch: connected device is null");
            return;
        }
        MessageRouter router = MessageRouter.getRouter(connectedDevice);
        if (router == null) {
            Trace.info(TAG, "Can't send message to watch: router is null");
        } else {
            router.onRequest(request, null);
        }
    }
}
